2007-11-07  Diego Novillo  <dnovillo@google.com>

	PR 33870
	* tree.h (struct tree_struct_field_tag): Add field in_nested_struct.
	(SFT_IN_NESTED_STRUCT): Define.
	* tree-dfa.c (dump_subvars_for): Show offset of each
	sub-var.
	* tree-flow.h (struct fieldoff): Add field in_nested_struct.
	* tree-ssa-structalias.c (struct variable_info): Likewise.
	(push_fields_onto_fieldstack): If OFFSET is positive,
	set in_nested_struct.
	(create_variable_info_for): Copy setting of
	in_nested_struct from the field offset object.
	(set_uids_in_ptset): Set SFT_IN_NESTED_STRUCT from the
	variable info object.
	* tree-ssa-operands.c (add_vars_for_offset): If VAR
	belongs to a nested structure, adjust OFFSET by
	SFT_OFFSET(VAR).

testsuite/ChangeLog

	* gcc.c-torture/execute/pr33870.x: Remove.

Index: tree.h
===================================================================
--- tree.h	(revision 129956)
+++ tree.h	(working copy)
@@ -2573,15 +2573,21 @@ struct tree_struct_field_tag GTY(())
   /* Size of the field.  */
   unsigned HOST_WIDE_INT size;
 
+  /* True if this SFT is for a field in a nested structure.  */
+  unsigned int in_nested_struct : 1;
+
   /* Alias set for a DECL_NONADDRESSABLE_P field.  Otherwise -1.  */
   alias_set_type alias_set;
 };
+
 #define SFT_PARENT_VAR(NODE) (STRUCT_FIELD_TAG_CHECK (NODE)->sft.parent_var)
 #define SFT_OFFSET(NODE) (STRUCT_FIELD_TAG_CHECK (NODE)->sft.offset)
 #define SFT_SIZE(NODE) (STRUCT_FIELD_TAG_CHECK (NODE)->sft.size)
 #define SFT_NONADDRESSABLE_P(NODE) \
   (STRUCT_FIELD_TAG_CHECK (NODE)->sft.alias_set != -1)
 #define SFT_ALIAS_SET(NODE) (STRUCT_FIELD_TAG_CHECK (NODE)->sft.alias_set)
+#define SFT_IN_NESTED_STRUCT(NODE) \
+  (STRUCT_FIELD_TAG_CHECK (NODE)->sft.in_nested_struct)
 
 /* Memory Partition Tags (MPTs) group memory symbols under one
    common name for the purposes of placing memory PHI nodes.  */
Index: testsuite/gcc.c-torture/execute/pr33870.x
===================================================================
--- testsuite/gcc.c-torture/execute/pr33870.x	(revision 129956)
+++ testsuite/gcc.c-torture/execute/pr33870.x	(working copy)
@@ -1,9 +0,0 @@
-# The test breaks because of wrong alias info for -O2 and -Os
-
-set torture_eval_before_compile {
-  if {[string match {*-O[2s]*} "$option"]} {
-     set torture_execute_xfail "*-*-*"
-  }
-}
-
-return 0
Index: tree-dfa.c
===================================================================
--- tree-dfa.c	(revision 129956)
+++ tree-dfa.c	(working copy)
@@ -287,7 +287,7 @@ dump_subvars_for (FILE *file, tree var)
   for (i = 0; VEC_iterate (tree, sv, i, subvar); ++i)
     {
       print_generic_expr (file, subvar, dump_flags);
-      fprintf (file, " ");
+      fprintf (file, "@" HOST_WIDE_INT_PRINT_UNSIGNED " ", SFT_OFFSET (subvar));
     }
 
   fprintf (file, "}");
Index: tree-flow.h
===================================================================
--- tree-flow.h	(revision 129956)
+++ tree-flow.h	(working copy)
@@ -1159,6 +1159,10 @@ struct fieldoff
   /* Field.  */
   tree decl;
 
+  /* True if this field is inside a structure nested inside the base
+     containing object.  */
+  unsigned int in_nested_struct : 1;
+
   /* Offset from the base of the base containing object to this field.  */
   HOST_WIDE_INT offset;  
 
Index: tree-ssa-structalias.c
===================================================================
--- tree-ssa-structalias.c	(revision 129956)
+++ tree-ssa-structalias.c	(working copy)
@@ -253,6 +253,15 @@ struct variable_info
      variable.  This is used for C++ placement new.  */
   unsigned int no_tbaa_pruning : 1;
 
+  /* True if this variable is inside a structure nested in the
+     structure for the base variable.  For instance, in 
+     struct X { int a; struct Y { int b; int c; } }, the variables for
+     fields 'b' and 'c' are inside a nested structure.  We are not
+     interested in tracking how many levels of nesting, just whether
+     there is nesting at all.  This is later used to adjust offsets
+     for pointers pointing into sub-structures.  */
+  unsigned int in_nested_struct : 1;
+
   /* Points-to set for this variable.  */
   bitmap solution;
 
@@ -4133,6 +4142,12 @@ push_fields_onto_fieldstack (tree type, 
 		pair->alias_set = get_alias_set (addressable_type);
 	      else
 		pair->alias_set = -1;
+
+	      /* If the base offset is positive, this field belongs to
+		 a structure nested inside the base structure.  */
+	      if (offset > 0)
+		pair->in_nested_struct = true;
+
 	      count++;
 	    }
 	  else
@@ -4181,6 +4196,12 @@ push_fields_onto_fieldstack (tree type, 
 	      pair->alias_set = get_alias_set (addressable_type);
 	    else
 	      pair->alias_set = -1;
+
+	    /* If the base offset is positive, this field belongs to
+	       a structure nested inside the base structure.  */
+	    if (offset > 0)
+	      pair->in_nested_struct = true;
+
 	    count++;
 	  }
 	else
@@ -4491,6 +4512,7 @@ create_variable_info_for (tree decl, con
 	  newvi->offset = fo->offset;
 	  newvi->size = TREE_INT_CST_LOW (fo->size);
 	  newvi->fullsize = vi->fullsize;
+	  newvi->in_nested_struct = fo->in_nested_struct;
 	  insert_into_field_list (vi, newvi);
 	  VEC_safe_push (varinfo_t, heap, varmap, newvi);
 	  if (is_global && (!flag_whole_program || !in_ipa_mode))
@@ -4743,6 +4765,7 @@ set_uids_in_ptset (tree ptr, bitmap into
 		      || (!is_derefed && !vi->directly_dereferenced)
 		      || alias_sets_conflict_p (ptr_alias_set, var_alias_set))
 		    bitmap_set_bit (into, DECL_UID (sft));
+		  SFT_IN_NESTED_STRUCT (sft) = vi->in_nested_struct;
 		}
 	    }
 	  else
@@ -4946,7 +4969,6 @@ find_what_p_points_to (tree p)
 	    }
 
 	  /* Share the final set of variables when possible.  */
-
 	  finished_solution = BITMAP_GGC_ALLOC ();
 	  stats.points_to_sets_created++;
 
Index: tree-ssa-operands.c
===================================================================
--- tree-ssa-operands.c	(revision 129956)
+++ tree-ssa-operands.c	(working copy)
@@ -1397,8 +1397,48 @@ add_vars_for_offset (tree var, unsigned 
   subvar_t sv;
   unsigned int i;
 
-  /* Adjust offset by the pointed-to location.  */
-  offset += SFT_OFFSET (var);
+  if (SFT_IN_NESTED_STRUCT (var))
+    {
+      /* Since VAR is an SFT inside a nested structure, the OFFSET
+	 computed by get_ref_base_and_extent is the offset from the
+	 start of the immediately containing structure.  However, to
+	 find out what other SFTs are affected by this reference, we
+	 need to know the offsets starting at the root structure in
+	 the nesting hierarchy.
+
+	 For instance, given the following structure:
+
+	 	struct X {
+		  int a;
+		  struct Y {
+		    int b;
+		    struct Z {
+		      int c[3];
+		    } d;
+		  } e;
+		} m;
+
+	 and the following address expression:
+
+		p_1 = &m.e.d;
+
+	 This structure will receive 5 SFTs, namely 2 for fields 'a'
+	 and 'b' and 3 for the array 'c' in struct Z.  So, the
+	 reference p_1->c[2] and m.e.d.c[2] access the exact same
+	 memory location (ie, SFT.5).
+
+	 Now, alias analysis computed the points-to set for pointer
+	 p_1 as  { SFT.3 } because that is the first field that p_1
+	 actually points to.  When the expression p_1->c[2] is
+	 analyzed, get_ref_base_and_extent will return an offset of 96
+	 because we are accessing the third element of the array.  But
+	 the SFT we are looking for is actually at offset 160,
+	 counting from the top of struct X.
+
+	 Therefore, we adjust OFFSET by the offset of VAR so that we
+	 can get at all the fields starting at VAR.  */
+      offset += SFT_OFFSET (var);
+    }
 
   /* Add all subvars of var that overlap with the access.
      Binary search for the first relevant SFT.  */
